feat(copilot): add GitHub Copilot provider (via @github/copilot-sdk) - #7656
feat(copilot): add GitHub Copilot provider (via @github/copilot-sdk)#7656NSExceptional wants to merge 1 commit into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| const generateThreadTitle: TextGenerationShape["generateThreadTitle"] = Effect.fn( | ||
| "CopilotTextGeneration.generateThreadTitle", | ||
| )(function* (input) { | ||
| const { prompt, outputSchema } = buildThreadTitlePrompt({ |
There was a problem hiding this comment.
🟡 Medium textGeneration/CopilotTextGeneration.ts:202
The Copilot provider ignores configured generation context: generateThreadTitle drops previousTitle, while generateBranchName, generatePrContent, and generateCommitMessage drop their respective policy values; generatePrContent also drops changeRequestTemplate. As a result, title regeneration uses the initial-title prompt, and branch names, PR content, and commit messages are generated without the caller's naming, template, or instruction constraints. Pass these omitted fields through to the corresponding build*Prompt calls.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/textGeneration/CopilotTextGeneration.ts around line 202:
The Copilot provider ignores configured generation context: `generateThreadTitle` drops `previousTitle`, while `generateBranchName`, `generatePrContent`, and `generateCommitMessage` drop their respective `policy` values; `generatePrContent` also drops `changeRequestTemplate`. As a result, title regeneration uses the initial-title prompt, and branch names, PR content, and commit messages are generated without the caller's naming, template, or instruction constraints. Pass these omitted fields through to the corresponding `build*Prompt` calls.
There was a problem hiding this comment.
Effect Service Conventions — 6 findings
The new Copilot provider code mostly follows the repo's Effect conventions, but a few error-handling details in the new files diverge from them (and from the sibling Grok/Cursor/Aether implementations):
CopilotSdkErroris aData.TaggedErrorwhose only payload is a stringified cause.CopilotTextGenerationhand-rolls a_tagpredicate, adds a pass-through error factory, and usesEffect.catchTaginstead ofEffect.catchTags.CopilotDriver/CopilotAdapterfoldcause.messageinto the caller-visibledetail.
Details inline.
Posted via Macroscope — Effect Service Conventions
56d5f9a to
c10b8ef
Compare
|
Thanks for the automated review — addressed the findings (force-pushed Correctness
Effect conventions
One I left as-is: the |
There was a problem hiding this comment.
One outstanding convention issue remains in the Copilot SDK error-wrapping path. Earlier findings on CopilotTextGeneration.ts (redundant error factory, hand-rolled _tag predicate, catchTag) are addressed. The still-open threads on CopilotDriver.ts:160 and CopilotSdkClient.ts:32-45 also remain applicable.
Posted via Macroscope — Effect Service Conventions
c10b8ef to
45ca7b9
Compare
|
Second pass on the re-review (
Typecheck + lint clean, 76 tests green. |
| makeProviderMaintenanceCapabilities({ | ||
| provider: DRIVER_KIND, | ||
| packageName: null, | ||
| updateExecutable: "copilot", |
There was a problem hiding this comment.
🟡 Medium Drivers/CopilotDriver.ts:53
The update action ignores an instance's configured binaryPath, so custom or absolute Copilot installations are updated using the PATH-resolved copilot binary—or fail when copilot is not on PATH. makeStaticProviderMaintenanceResolver always returns the hard-coded executable from UPDATE; use a resolver that derives the update executable from the supplied binaryPath.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/CopilotDriver.ts around line 53:
The update action ignores an instance's configured `binaryPath`, so custom or absolute Copilot installations are updated using the PATH-resolved `copilot` binary—or fail when `copilot` is not on `PATH`. `makeStaticProviderMaintenanceResolver` always returns the hard-coded executable from `UPDATE`; use a resolver that derives the update executable from the supplied `binaryPath`.
|
Round 3 (
Not changed — flagging as consistent-with-siblings rather than a Copilot-specific bug:
Typecheck + lint clean, 77 tests green. |
45ca7b9 to
1e7bc57
Compare
|
Round 4 ( |
1e7bc57 to
a91e1c5
Compare
There was a problem hiding this comment.
One retained convention issue in the new Copilot SDK layer: CopilotSdkError is still modeled with Data.TaggedError and its detail is a stringified cause. See the inline note. (The earlier findings in CopilotTextGeneration.ts and CopilotAdapter.ts look addressed.)
Posted via Macroscope — Effect Service Conventions
|
Round 5 ( I believe that leaves only the |
a91e1c5 to
6d40d6e
Compare
|
Round 6 ( |
6d40d6e to
6cd5f28
Compare
There was a problem hiding this comment.
One retained convention issue in apps/server/src/provider/sdk/CopilotSdkClient.ts: the wrapper's message is still derived from a detail field that copies cause.message. The previously flagged detail interpolation in apps/server/src/provider/Drivers/CopilotDriver.ts:160 is also still open (existing comment).
Posted via Macroscope — Effect Service Conventions
There was a problem hiding this comment.
UI consistency review of the changed web files (providerIconUtils.ts, AddProviderInstanceDialog.tsx, providerDriverMeta.ts, session-logic.ts).
Provider registration is consistent: the new copilot driver is added to PROVIDER_CLIENT_DEFINITIONS, PROVIDER_ICON_BY_PROVIDER and PROVIDER_OPTIONS, the stale githubCopilot "Coming Soon" entry is removed alongside its now-unused icon import, and GithubCopilotIcon uses the same fill-black dark:fill-white treatment as the other brand icons, so light/dark tone matches the existing provider rows. No primitive reconstruction, class-override, or CSS-ownership issues found.
One finding: unrelated re-wrapping in providerDriverMeta.ts (details inline).
Posted via Macroscope — UI Consistency
|
Round 7 (
|
6cd5f28 to
525c76b
Compare
|
All three Macroscope review checks are green now (Correctness, Effect Service Conventions, UI Consistency). The only remaining red check is ^ Leave it to Claude to make my comments sound AI-generated as hell |
There was a problem hiding this comment.
Effect service conventions: one new finding plus one previously-flagged issue still present.
apps/server/src/provider/Layers/CopilotProvider.ts:314— arbitrary defect text interpolated into the caller-visible probemessage(commented inline).apps/server/src/provider/Drivers/CopilotDriver.ts:160—ProviderDriverError.detailis still built fromcause.message;messagemust be derived from stable structural attributes only (already flagged on an earlier revision, so not re-commented).
Posted via Macroscope — Effect Service Conventions
|
Round 11 (
Typecheck + lint clean. |
3c1e448 to
3a93557
Compare
|
Round 12 (
Typecheck + lint clean, 534 provider tests green. |
3a93557 to
0b546b1
Compare
|
Round 13 ( |
0b546b1 to
21f5b95
Compare
|
Round 14 (
Typecheck + lint clean. |
21f5b95 to
0c26e20
Compare
|
Round 15 ( (Also in this push: binary resolver now rejects directories via |
0c26e20 to
92461c7
Compare
| Semaphore.make(1).pipe( | ||
| Effect.map((semaphore) => { | ||
| const next = new Map(current); | ||
| next.set(threadId, semaphore); |
There was a problem hiding this comment.
🟡 Medium Layers/CopilotAdapter.ts:238
threadLocksRef retains a semaphore and the threadId for every thread ever passed to withThreadLock, so a long-running server leaks one map entry per historical thread even after sessions stop or startSession fails. Remove entries once no operation can use the thread lock, while preserving synchronization for concurrent operations.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/CopilotAdapter.ts around line 238:
`threadLocksRef` retains a semaphore and the `threadId` for every thread ever passed to `withThreadLock`, so a long-running server leaks one map entry per historical thread even after sessions stop or `startSession` fails. Remove entries once no operation can use the thread lock, while preserving synchronization for concurrent operations.
|
Round 16 (
Typecheck + lint clean, 596 provider/text-gen tests green. Two I'm leaving as consistent-with-siblings (not Copilot-specific), flagging rather than diverging one adapter:
|
92461c7 to
aa37d5d
Compare
aa37d5d to
636565e
Compare
|
Round 17 (
Typecheck + lint + fmt clean; 20 Copilot provider/SDK tests green. On the one item I've left as-is: @NaveDanan — thanks again for the thorough parity pass; that list was exactly right. The one thing I haven't added is a dedicated adapter test harness for concurrent turns / interruption / startup failure: the sibling adapter tests drive a mock ACP subprocess, which doesn't apply to the SDK transport, so that needs a mock copilot-sdk runtime — flagging it as a follow-up rather than blocking this on it. |
636565e to
e38346a
Compare
|
Round 18 ( The reported finding ( Correctness fixes found while reviewing the above
Deliberately not done: a blanket per-turn timeout on Typecheck (0 errors, no new warnings) + lint + fmt clean; 20 provider/SDK tests green. |
Adds GitHub Copilot as a first-class provider, driven by the first-party
`@github/copilot-sdk`. The SDK spawns and drives the installed `copilot`
runtime binary over its typed JSON-RPC protocol (`RuntimeConnection.forStdio`),
so no extra runtime download is required.
Highlights:
- Provider / driver / adapter under `apps/server/src/provider/` plus a small
`provider/sdk/` layer:
- `CopilotSdkClient` — scoped Effect wrapper around `CopilotClient`.
- `CopilotSdkModels` — maps `client.listModels()` to per-model capabilities:
reasoning effort from each model's `supportedReasoningEfforts`, and a
context-window tier gated on the model's `longContext` billing block.
- `CopilotSdkRuntimeEvents` — translates SDK `SessionEvent`s into the
canonical `ProviderRuntimeEvent` stream.
- Session lifecycle: one shared client, per-thread `CopilotSession`, a
callback→Effect event bridge, permission requests wired into the existing
approval flow, and `send` + `session.idle` turn handling. Reasoning effort
and context tier are applied via `SessionConfig` / `session.setModel`.
- Model discovery via `client.listModels()`.
- Git text generation (commit messages, PR content, branch names, thread
titles) uses the SDK's one-shot `sendAndWait`.
- Contracts: `copilot.sdk.event` / `copilot.sdk.permission` runtime sources and
Copilot settings/model schemas; web UI wiring for settings + model picker.
- Resolves the `copilot` binary to an absolute path before spawning (a GUI app
inherits a minimal PATH), and passes env only on the stdio connection.
Co-authored-by: its-hmny <enea.guidi@n26.com>
e38346a to
e0795ed
Compare
|
Round 19 (
The residuals are narrow and, I think, acceptable: an unacknowledged Typecheck (0 errors, no new warnings) + lint + fmt clean; 20 provider/SDK tests green. |
|
I opened a stacked follow-up PR: NSExceptional/t3code#1. While testing the provider, I found two integration gaps:
The follow-up adds skill metadata discovery, built-in command discovery, command invocation, command-result rendering, and focused regression tests. I tested |
|
Note 🤖 GPT-5.6 Sol responding on behalf of Theo Closing this PR after an automated pass over open pull requests. Outside Copilot integration that competes with three other outside proposals and product-owned provider work. |
|
@t3dotgg can you elaborate on what that means? Is this something only you or the core team will be able to implement? Or is it something you have in progress already? |
What
Adds GitHub Copilot as a first-class provider, alongside Codex / Claude / Cursor / Grok / OpenCode. It's driven by GitHub's first-party
@github/copilot-sdk, which spawns and drives the installedcopilotruntime binary over its typed JSON-RPC protocol (RuntimeConnection.forStdio) — the same engine the Copilot CLI/IDE use. No extra runtime download; it reuses whatevercopilotthe user already has (e.g. Homebrew).How
apps/server/src/provider/, plus a smallprovider/sdk/layer:CopilotSdkClient— scoped Effect wrapper aroundCopilotClient(start on acquire, stop on release; one shared client per provider instance).CopilotSdkModels— mapsclient.listModels()into per-model capabilities: reasoning effort from each model'ssupportedReasoningEfforts, and a context-window tier gated on the model'slongContextbilling block. Applied viaSessionConfig/session.setModel.CopilotSdkRuntimeEvents— translates SDKSessionEvents into the canonicalProviderRuntimeEventstream.CopilotSession, a callback→Effect event bridge (SDK is callback-based, unlike the async-iterable providers), permission requests wired into the existing approval flow, andsend+session.idleturn handling.client.listModels().sendAndWait.copilot.sdk.event/copilot.sdk.permissionruntime sources and Copilot settings/model schemas; web wiring for the provider settings + model picker (reuses the generic option-descriptor UI).copilotbinary to an absolute path before spawning (a GUI-launched app inherits a minimal PATH), and passes env only on the stdio connection (the SDK rejects env in both places).Testing
tsgotypecheck clean (contracts / server / web); targeted lint clean.copilot1.0.80:createSession→ streamingsend→session.idle,setModelwithlong_context, and the permission callback.listModels()returns per-model reasoning efforts / context tiers as expected.Notes / open questions
Co-authored-by); I ported it to the SDK and reworked model discovery + tunables.Authored with Claude Opus 4.8 via Claude Code.
Note
Add GitHub Copilot provider via
@github/copilot-sdkcopilotprovider driver registered in builtInDrivers.ts, wiring together a SDK client, adapter, text generation, and provider snapshot pipeline.copilotkind, default togpt-4.1/gpt-4.1-mini, and surface the provider in pickers and settings dialogs.@github/copilot-sdkdependency and disallowskoffibuilds in pnpm-workspace.yaml;COPILOT_DRIVER_KINDdefaults in model.ts may affect model selection if overrides are absent.📊 Macroscope summarized 56d5f9a. 17 files reviewed, 16 issues evaluated, 2 issues filtered, 10 comments posted
🗂️ Filtered Issues
apps/server/src/provider/Layers/CopilotAdapter.ts — 3 comments posted, 5 evaluated, 1 filtered
nativeEventLogger, but never invokes itswritemethod. Therefore configuringnativeEventLogPathor injecting a native logger creates/manages the logger while every Copilot SDK event is silently omitted from the native event log. [ Out of scope (post-validation triage) ]apps/server/src/provider/Layers/CopilotProvider.ts — 2 comments posted, 3 evaluated, 1 filtered
detectCopilotAuthFromEnvironmentuses nullish coalescing before checking whether a token is nonempty. IfCOPILOT_GITHUB_TOKENis defined as""or whitespace whileGH_TOKENorGITHUB_TOKENcontains a valid token, the empty first value wins and the function returnsunknowninstead ofauthenticated. Select the first nonblank value rather than the first non-nullish one. [ Out of scope (post-validation triage) ]